#include "gameEngine.h"
#include <fstream>
#include <iostream>
#include <sstream>
GameEngine::GameEngine()
{
    frameCount=0;
    mesActions= new std::vector<AbstractSlot *>;
    SDLWindow= new SDLWindow();
}

void GameEngine::GameLoop()
{
    int continuer=1;

    while(continuer)
    {
        currentTime=SDL_GetTicks();

        if(currentTime-lastTime>15)
        {
            lastTime=currentTime;
            keyboardState=SDL_GetKeyState(NULL);

             //    SDL_WaitEvent(&event);	//	Méthode qui attends qu'un événement se produise, blocante
            while(SDL_PollEvent(&event))        //	Méthode qui se déclenche lors qu'un événement se produit, non blocante
            {
                if(event.type==SDL_QUIT)
                {
                    continuer=0;
                }
                InputEventManagement();
            }
            EventManagement();
            for(int i=0;i<elements.size();i++)
            {
                if(elements[i]->state==0)
                {
                    elements.erase(elements.begin()+i);
                }
                else
                {
                    elements.at(i)->Update();
                }
            }
            CheckCollision();
            DoAction();
            CalcxulPosition();
            DisplayFrame();
            SDL_GL_SwapBuffers();
            frameCount++;
        }
        else /* Si ça fait moins de 30ms depuis le dernier tour de boucle, on endort le programme le temps qu'il faut */
        {
            SDL_Delay(15 - (currentTime - lastTime));
        }
    }
}


void GameEngine::InputEventManagement()
{
    for(int i=0;i<elements.size();i++)
    {
        elements.at(i)->CheckInputDelegates();
    }
}

void GameEngine::DoAction()
{
    for(int i=0;i<mesActions->size();i++)
    {
        mesActions->at(i)->Run();
        if(mesActions->at(i).end)
        {
            mesActions->erase(mesActions->begin()+i);
        }
    }

}

void GameEngine::EventManagement()
{

    for(int i=0;i<delegates.size();i++)
    {
        delegates.at(i)->Update();
    }
    for(int i=0;i<elements.size();i++)
    {
        elements.at(i)->CheckDelegates();
    }
}
/*
int GameEngine::ReadShaders(std::string fileName, GLint idShader)
{
    std::ifstream file(fileName.c_str(),std::ios::in);      /*!<    Ouverture du fichier en lecture */
/*
    std::vector<char *> lines;

    while(!file.eof())   /*!<  Segmente le fichier ligne par ligne    */
  /*  {
        char * line = new char[200];
        file.getline(tempChar,NULL);
        lines.push_back(line);
    }

    /*!< On transfert le contenu du vecteur dans un tableau **, tout simplement
     *   car la méthode permettant de créer le shader prend en argument un
     *   const char **
     */
/*
    char **allLines = new char *[lines.size()];

    for(int i=0;i<lines.size();i++)
    {
        allLines[0]=lines.at(i);
    }

    /*!
     *      \fn glShaderSource
     *      \brief prend en parmaètre : le GLint identifiant le shader,
     *      le nombre de ligne composant le code glsl,
     *      un tableau de tableau de caratère, correspondant au code
     *      La limite du nombre de caractere par ligne, peut prendre null
     */
/*
    glShaderSource(idShader,lines.size(), (const GLchar**)allLines,NULL);

    glCompileShader(idShader);
}*/

void GameEngine::InitShaders()
{
/*
    char ** cvf=new char * [1];
    cvf[0]=(char *)codeVertexShader.c_str();


    char ** cfs= new char *[1];
    cfs[0]=(char *)codeFragmentShader.c_str();

    GLuint vertexShader =   glCreateShader(GL_VERTEX_SHADER);      //  Création des deux shaders
    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);

    GLuint

    glShaderSource(vertexShader,1, (const GLchar**)cvf,NULL);
    glShaderSource(fragmentShader, 1,(const GLchar**)cfs, NULL);


    glCompileShader(fragmentShader);

    GLuint program= glCreateProgram();
    glAttachShader (program, fragmentShader);
    glAttachShader (program, vertexShader);

    glLinkProgram(program);
    glUseProgram( program);

    GLint uniformModelViewMatrix= glGetUniformLocation(program,"modelViewMatrix");  /*!< On lie le nom de variable représentant
                                                                                    /*   la matrice de mdélisation */
/*

    GLint uniformProjectionMatrix = glGetUniformLocation(program,"projectionMatrix");

    glUniform4f(uniforId, 1,val);
    GLint  uniformVar =glGetUniformLocation(program, "variableName");

    GLint glGetAttribLocation(GLuint program,char *name);
    void glVertexAttrib1f(GLint location, GLfloat v0);

    void glEnableVertexAttribArray(GLint loc);
    void glVertexAttribPointer(GLint loc, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);

*/
}

void GameEngine::InitLighting()
{
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    float lightSpecular[3]={1.0, 1.0, 1.0};
    float lightDiffuse[3] ={0.8, 0.8, 0.8} ;
    float lightAmbiant[3] ={0.3, 0.3, 0.3};

    light_position =  new float[4];
    light_position[0]=1.0; light_position[1]=10.0; light_position[2]=1.0, light_position[3]=1.0;
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    glLightfv(GL_FRONT,GL_AMBIENT,lightAmbiant);
    glLightfv(GL_FRONT,GL_SPECULAR,lightSpecular);
    glLightfv(GL_FRONT,GL_DIFFUSE,lightDiffuse);
}

void GameEngine::InitGL()
{
   // InitShaders();
    InitLighting();

    glClearColor(0.0, 0.2, 0.7, 0);
    glEnable(GL_DEPTH_TEST);
    glEnable (GL_BLEND);

    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glFrontFace(GL_CCW);

    GLfloat mat_diffuse [4] = {0.6, 0.6, 0.6, 1.0};
    GLfloat mat_specular [4] = {0.9, 0.9, 0.9, 1.0};
    GLfloat mat_shininess [1] = {100.0};

    glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
    glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
    glMaterialfv (GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);

}

void GameEngine::DisplayFrame()
{
    glMatrixMode(GL_MODELVIEW);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    pow.SetPOW();

    for(int i=0;i<elements.size();i++)
    {
        elements.at(i)->Display();
    }
}



void GameEngine::InitOrthoProjection(float aLowerBoundx, float aUpperBoundx, float aLowerBoundy, float aUpperBoundy)
{
    dimension=2;
    lowerBoundGLx=aLowerBoundx;upperBoundGLx=aUpperBoundx;lowerBoundGLy=aLowerBoundy;upperBoundGLy=aUpperBoundy;

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(lowerBoundGLx, upperBoundGLx, lowerBoundGLy, upperBoundGLy, -1, 1000);
}

void GameEngine::InitFrustsumProjection()
{
    dimension=3;
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(90.0f, 1.0f, 1.0f, 500.0f);
}


void GameEngine::SetGravity()
{
    for(int i=0;i<elements.size();i++)
    {
        if(elements[i]->movable==1)
        {
            if(elements[i]->gravity)
            {
                elements[i]->velocity->AddAcceleration(new Vectorf(0.0,elements[i]->valGravity,0.0));
            }
        }
    }
}

void GameEngine::CheckCollision()
{
    for(int i=0; i<collisions.size();i++)
    {
        collisions.at(i)->CheckCollision();
    }
}

void GameEngine::CalculPosition()
{
    for(int i=0;i<elements.size();i++)
    {
        if(elements[i]->movable==1)
        {
            elements[i]->velocity->Do();
        }
    }
}

void GameEngine::AddElement(GraphicElement * aObject)
{
    aObject->event=&event;
   // aObject->SetPicking(0,0,elements.size()+1);
    elements.push_back(aObject);
}


void GameEngine::DisplayPicking()
{
    glMatrixMode(GL_MODELVIEW);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    glDisable(GL_LIGHTING);
    glDisable(GL_LIGHT0);
    pow.SetPOW();

    for(int i=0;i<elements.size();i++)
    {
        if(elements[i]->picking==1)
        {
            elements[i]->Picking();
        }
    }
    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);
}

void GameEngine::DefineCamera()
{
    AbstractDelegate * delegate = new Delegate<GameEngine>(&GameEngine::sMM,this);
    delegate->Connect(new Slot<GameEngine>(&GameEngine::MoveCamera,this));
    delegates.push_back(delegate);
}
